package org.cakefoundation.flexcake.business
{
	import flash.errors.IllegalOperationError;
	import flash.events.*;
	import flash.net.*;
	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	
	import mx.collections.*;
	import mx.controls.Alert;
	import mx.core.IMXMLObject;
	import mx.managers.CursorManager;
	import mx.rpc.AbstractService;
	import mx.rpc.AsyncToken;
	import mx.rpc.Fault;
	import mx.rpc.IResponder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.mxml.IMXMLSupport;
	import mx.utils.ArrayUtil;

	use namespace flash_proxy;

	[Event(name="fault", type="mx.rpc.events.FaultEvent")]	
	[Event(name="result", type="mx.rpc.events.ResultEvent")]
	[Bindable]
	dynamic public class CakeService extends AbstractService implements IMXMLObject, IMXMLSupport {

		public var makeObjectsBindable:Boolean = false;
		public var endpoint:String;	
		public var source:String;
		public var gateway_conn : NetConnection;
		private var _view:Object;
		private var _id:String;
		protected var _concurrency:String;
		protected var _showBusyCursor:Boolean;	

		public var _methodResultArray:Array;
		public var _methodResponderArray:Array;		
		
		// this replaces the _methodResponderArray
		static public var callList:Dictionary;

		public var results:*;

		public function CakeService() {
			_methodResultArray = new Array();
			_methodResponderArray = new Array();
			if( !CakeService.callList )
			{
				CakeService.callList = new Dictionary();
			}
		}

		
		flash_proxy override function callProperty( methodName:*, ...args):* {

			if( gateway_conn == null || concurrency == "single" )
			{
            	gateway_conn = new NetConnection();
            	gateway_conn.connect( endpoint );
   			}
			
            var respond:CakeResponder = new CakeResponder( methodName, setQueryResult, setQueryFault );
            var async:AsyncToken = respond.getAsyncToken();
            var parameters:Array = ArrayUtil.toArray(args)
            var cakeResponder:ICakeResponder;
            // get the Responder from call and remove it from the args.
            if(parameters[0] is ICakeResponder){
            	trace('set up cakeResponder from args');
            	cakeResponder = parameters.shift();
            } else {
            	cakeResponder = new CakeResponder(methodName,null,null);
            }
            // add cakeReponder to token which in turn causes the results to be passed back to it
			async.addResponder( cakeResponder );
			
		
			
			
			// add CakeResponder to static call list
			CakeService.callList[ async.message.messageId ] = respond;
			
			var callString:String = source + "." + methodName.toString();
			
			// call service.  
			if( parameters.length > 0 )
			{
        		gateway_conn.call(callString, respond, parameters );
   			}
   			else
   			{
   				gateway_conn.call(callString, respond );
   			}
			// set Busy Cursor
			cursor();
			trace("CakeService::create "+async.message.messageId );
			
	        return async;
		}
		
		public function log_out():AsyncToken
		{
			use namespace flash_proxy;
			
			var respond:CakeResponder = new CakeResponder( 'logout', setQueryResult, setQueryFault );
			var async:AsyncToken = respond.getAsyncToken();
			
			CakeService.callList[ async.message.messageId ] = respond;
			
			gateway_conn.call( source + ".logout" , respond );
			
			super.logout();
			
			return async;
		}
		
		flash_proxy override function getProperty( name:* ):* {
			if( _methodResultArray[ name ] != null )
				return _methodResultArray[ name ];
		}		
		
		flash_proxy override function setProperty(name:*, value:*):void {
			if( name == "destination" )
				endpoint = value;
			else
				_methodResultArray[ name ] = value;
		}

		flash_proxy override function getDescendants( name:* ):* {
		
		}
		
		public function initialized( view:Object, id:String  ):void {
			_view = view;
			_id = id;
		}
		/**
		 * I have switched this to use the messageId of the AsyncronousToken as this is a unique value.
		 */ 
        private function setQueryResult( messageId:String, result:Object ):void {
        	// cast to ArrayCollection.
        	//TODO: this is legacy from Renaun's code.  I may remove it ..
			results = bindableFilter( result );
			trace( "CakeService::setQueryResult "+messageId);

			var respond:CakeResponder = CakeService.callList[ messageId ];
			var asyncToken:AsyncToken = respond.getAsyncToken();
			
			var resultEvent:ResultEvent = new ResultEvent( ResultEvent.RESULT, 
											false, 
											true, 
											results, 
											asyncToken, 
											asyncToken.message )
			/**
			 * this loop checks for Error objects and passes them to the ICakeResponder.error()
			 * method. Otherwise the result is passed to the ICakeResponder.result() method
			 * 
			 * @see com.iamsuperfly.interface.ICakeResponder
			 */ 
			if( asyncToken.hasResponder() ) 
			{
				trace('passing responce to responder');
				for( var i:int = 0; i < asyncToken.responders.length; i++ )
				{
					trace('loop '+i+':');
					if( resultEvent.result.hasOwnProperty('Error') )
					{
//						asyncToken.responders[ i ].error( resultEvent.result );
					trace('error');
					}
					else
					{
						trace('result:'+ String(result));
						asyncToken.responders[ i ].result( resultEvent.result );
					}
				}
			}
			
			dispatchEvent( resultEvent );
			// remove Busy Cursor
			cursor(false);
			// remove responder from list
			delete CakeService.callList[ messageId ];		
        }

        private function setQueryFault( messageId:String, fault:Object ):void {
            var respond:CakeResponder = CakeService.callList[ messageId ];
            var code:String = "unknown fault";
            if( fault.code != null )
            	code = String( fault.code );
            var faultString:String = "";
            if( fault.level != null )
            	faultString = String( "level: " + fault.level );
            var description:String = "";
            if( fault.description != null )
            	description = String( fault.description );            	            

			var faultEvent:FaultEvent = new FaultEvent( FaultEvent.FAULT, 
            								false, 
            								false, 
            								new Fault(code,faultString,description),
            								respond.getAsyncToken(), 
            								respond.getAsyncToken().message )

			var asyncToken:AsyncToken = respond.getAsyncToken();

			if( asyncToken.hasResponder() ) {
				for( var i:int = 0; i < asyncToken.responders.length; i++ )
					asyncToken.responders[ i ].fault( faultEvent );
			}			
            	
            dispatchEvent( faultEvent );
			// remove Busy Cursor
			cursor(false);
				
            // remove responder from list
			delete CakeService.callList[ messageId ];							
        }
        
        private function cursor(busy:Boolean = true):void
        {
        	if( _showBusyCursor ){
        		if(busy){
        			CursorManager.showCursor();
        		}else{
					CursorManager.removeBusyCursor();
        		}
        	}
        }

        /**
         *  If makeObjectsBindable filter.  If returned object is an Array auto make it an ArrayCollection
         */
        private function bindableFilter( result:Object ):* {
        	if( this.makeObjectsBindable ) {
	        	if( result is Array ) {
	        		return new ArrayCollection( ArrayUtil.toArray( result ) );
	        	} else if( result is Object ) {
	        		// Just do one level deep searching for Array types
	        		for( var a:String in result ) {
	        			if( result[ a ] is Array )
	        				result[ a ] = new ArrayCollection( ArrayUtil.toArray( result[ a ] ) );
	        		}
	        	}
        	}
            return result;
        }	

        /**
         *  Define public getter method.
         */
        public function get concurrency():String {
            return _concurrency;
        }         

        
        /**
         *  Define public getter method.
         */
        public function set concurrency( value:String ):void {
            _concurrency = value;
        }

        /**
         *  Define public getter method.
         */
        public function get showBusyCursor():Boolean {
            return _showBusyCursor;
        }
        
        /**
         *  Define public getter method.
         */
        public function set showBusyCursor( value:Boolean ):void {
            _showBusyCursor = value;
        } 
									
	}
}

